/**
* \file Combinaison.c
* \brief Comprend les fonctions et les procédures qui permettent la gestion de combinaisons
*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

#include "Parametres.h"
#include "Combinaison.h"

#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>

/**
* \fn void initCombinaison(Combinaison* comb, Parametres param)
* \brief La procédure initCombinaison alloue un tableau d'entiers pour stocker la combinaison
*        et initialise la combinaison avec des 0.
*/

void initCombinaison(Combinaison* comb, Parametres param)
{
    int i;
    i=0;
    comb-> tab = (int *) malloc((param.taille)*sizeof(int));
    comb->taille=param.taille;

    for (i=0; i<param.taille; i++)
    {
        comb-> tab[i] = 0;
    }
}


/**
* \fn void libereCombinaison(Combinaison* comb)
* \brief La procédure libereCombinaison libère le tableau d'entiers réservé à stocker la combinaison
*        et remet le pointeur sur tableau à NULL.
*/

void libereCombinaison(Combinaison* comb)
{
    free (comb->tab);
    comb-> tab = NULL;
    comb-> taille = 0;

}


/**
* \fn int longueurEntier (int nb)
* \brief La fonction longueurEntier calcule la longueur d'un entier
* \details ex: 124 --> longueur = 3
* \return Longueur de l'entier
*/

int longueurEntier (int nb)
{
	int longueur;
	longueur = 0;

	while(nb !=0)
	{
		longueur++;
		nb = nb / 10;
	}

	return longueur;
}


/**
* \fn void saisieCombinaison(Combinaison* comb, Parametres param)
* \brief La procédure saisieCombinaison permet la saisie d'une combinaison.
*        Elle fait appel à une deuxième fonction pour décomposer l'entier sous forme de combinaison.
*/

void saisieCombinaison(Combinaison* comb, Parametres param)
{
	int combTestee;
	int respecte;
	combTestee = 0;

    printf ("Entrez la combinaison à tester (chiffres de 1 à %d) : \n", param.nbCouleursUtilisables);
    scanf ("%d", &combTestee);
    respecte = parametresRespectes(combTestee, param);

    if(respecte == 1)
    {
        saisieCombinaisonBis(comb, param, combTestee);
    }

    else
    {
        printf ("La combinaison n'est pas valide veuillez recommencer.\n");
        saisieCombinaison(comb, param);
    }
}



/**
* \fn void saisieCombinaisonSecreteTexte(Combinaison* comb, Parametres param)
* \brief La procédure saisieCombinaisonSecreteTexte permet, dans le mode 2 joueurs,
*        la saisie de la combinaison secrète par un joueur.
*        Elle fait appel à une deuxième fonction pour décomposer l'entier sous forme de combinaison.
*/

void saisieCombinaisonSecreteTexte(Combinaison* comb, Parametres param)
{
    int combTestee;
    int respecte;
    combTestee = 0;

    printf ("Entrez la combinaison secrète (chiffre de 1 à %d) : \n", param.nbCouleursUtilisables );
    scanf ("%d", &combTestee);
    respecte=parametresRespectes(combTestee, param);

    if(respecte==1)
    {
        saisieCombinaisonBis (comb, param, combTestee);
    }

    else
    {
        printf ("La combinaison n'est pas valide veuillez recommencer.\n");
        saisieCombinaisonSecreteTexte(comb, param);
    }
}



/**
* \fn void saisieCombinaisonBis(Combinaison* comb, Parametres param, int combTest)
* \brief La procédure saisieCombinaisonBis permet de décomposer un entier
*        pour le rendre sous forme de combinaison.
* \details ex: si l'entier (combTest) est 123, la combinaison sera 1 2 3.
*/

void saisieCombinaisonBis(Combinaison* comb, Parametres param, int combTest)
{
    int i;
    int modulo;
    int quotient=combTest;
    assert ((comb->taille == param.taille) && (comb->taille == longueurEntier (combTest)));

	i=comb->taille-1;

    for(i=comb->taille-1; i>=0; i--)
    {
        modulo=quotient%10;
        assert((modulo>=1) &&(modulo <= param.nbCouleursUtilisables));
        comb->tab[i] = modulo;
        quotient=quotient/10;
    }

}



/**
* \fn int pionsBienPlaces(Combinaison* comb1, Combinaison* comb2)
* \brief La fonction pionsBienPlaces compare deux combinaisons et retourne le nombre
*        d'éléments identiques placés au même endroit.
*/

int pionsBienPlaces(Combinaison* comb1, Combinaison* comb2)
{
    int i=0;
    int j=0;

    for(i=0; i<(comb1->taille); i++)
    {
        if((comb1->tab[i]) == (comb2->tab[i]))
        {
            j++;
        }
    }

    return (j);
}




/**
* \fn int pionsBonneCouleur(Combinaison* comb1, Combinaison* comb2, Parametres param)
* \brief La fonction pionsBonneCouleur compare deux combinaisons et retourne le nombre de pions de la première combinaison
* dont la couleur apparait dans la seconde combinaison mais ne sont pas placés au même endroit.
* Elle fait appel à une fonction récursive qui modifie les combinaisons de départ, c'est pour cela qu'on recopie les combinaisons
* de départ avant d'appeler la fonction récursive.
*/

int pionsBonneCouleur(Combinaison* comb1, Combinaison* comb2, Parametres param)
{
    Combinaison combinaison1;
    Combinaison combinaison2;
    int k;
    int i,j;

    initCombinaison(&combinaison1,param);
    initCombinaison(&combinaison2,param);

    for(i=0; i<combinaison1.taille; i++)
    {
        combinaison1.tab[i] = comb1->tab[i];
    }

    for(j=0; j<combinaison2.taille; j++)
    {
        combinaison2.tab[j] = comb2->tab[j];
    }

    k = pionsBonneCouleurBis(&combinaison1, &combinaison2) - pionsBienPlaces(comb1, comb2);

    libereCombinaison(&combinaison1);
    libereCombinaison(&combinaison2);

    return (k);
}


/**
* \fn int pionsBonneCouleurBis(Combinaison* comb1, Combinaison* comb2)
* \brief La fonction pionsBonneCouleurBis est une fonction récursive qui retourne le nombre d'éléments
*        communs entre deux combinaisons. Cette fonction modifie les combinaisons passées en paramètres.
*/

int pionsBonneCouleurBis(Combinaison* comb1, Combinaison* comb2)
{
    int j,k,l,m,n;
    k=0;

/* Cas d'arrêt: si la première combinaison n'a plus qu'un seul élément,
   on compte le nombre d'occurences de cet élément dans la deuxième combinaison. */
    if(comb1->taille == 1)
    {
        j=0;

        while(j<comb2->taille)
        {
            if(comb1->tab[0]==comb2->tab[j])
            {
                k++;
                j = comb2->taille + 1;
            }
            j++;
        }
    }

    else
    {
        j=0;
/* On cherche si le premier élément de la première combinaison apparait dans la seconde,
   si oui on supprime l'élément des deux combinaisons, et on recommence le traitement sur
   les deux combinaisons privées de l'élément précedemment supprimé. */
        while(j<comb2->taille)
        {

          if(comb1->tab[0] == comb2->tab[j])
          {
/* Si les éléments des deux cases sont égaux,
   on recopie les deux combinaisons en supprimant ces deux cases. */

/* Cette boucle sert à supprimer le premier élément de comb1 en décalant tous ses éléments d'un cran vers la gauche. */
               for(l=1; l<comb1->taille; l++)
               {
                    comb1->tab[l-1] = comb1->tab[l];
               }

/* Cette boucle sert à supprimer la case j de comb2 en décalant tous les éléments à partir
   de la case j+1 d'un cran vers la gauche. */
               for(m=j+1; m<comb2->taille; m++)
               {
                    comb2->tab[m-1] = comb2->tab[m];
               }
/* Quand on trouve deux cases égales, on fait j=comb2->taille pour sortir de la boucle while
   pour arrêter le parcours de comb2, afin de ne pas compter des éléments en double. */
                j = comb2->taille + 1;

                comb1->taille = (comb1->taille) - 1;
                comb2->taille = (comb2->taille) - 1;

/* k fait le compte des éléments communs entre comb1 et comb2,
   ici on ajoute 1 car la case 0 de comb1 et la case j de comb2 sont égales. */
                k = 1 + pionsBonneCouleurBis(comb1, comb2);
            }

/* Si la case 0 de comb1 et la case j de comb2 ne sont pas égales, on continue le parcous de comb2,
   donc on fait j++ pour passer à la case j+1 */
            else
            {
                j++;
            }

         }

/* Si j==comb2->taille, on est arrivé à la fin de comb2 sans trouver de correspondance pour la case 0
   de comb1 donc on la supprime. */
       if(j == comb2->taille)
       {
            for(n=1; n<comb1->taille; n++)
            {
                comb1->tab[n-1] = comb1->tab[n];
            }

            comb1->taille = comb1->taille - 1;
            k = pionsBonneCouleurBis(comb1, comb2);
       }
    }

    return (k);

}



/**
* \fn void generationAleatoireCombinaisonSecreteTexte(Combinaison* combinaisonSecrete, Parametres param)
* \brief La procédure generationAleatoireCombinaisonSecreteTexte génère une combinaison secrète de manière aléatoire.
* Elle sert pour le mode texte --> chiffre de 1 à 8.
*/

void generationAleatoireCombinaisonSecreteTexte(Combinaison* combinaisonSecrete, Parametres param)
{
    int i;
    initCombinaison(combinaisonSecrete, param);

    srand(time(NULL));
    for (i=0; i<combinaisonSecrete-> taille; i++)
    {
        combinaisonSecrete-> tab[i] = (rand()%param.nbCouleursUtilisables) + 1;
    }
}


/**
* \fn void generationAleatoireCombinaisonSecreteSDL(Combinaison* combinaisonSecrete, Parametres param)
* \brief La procédure generationAleatoireCombinaisonSecreteSDL génère une combinaison secrète de manière aléatoire.
*        Elle sert pour le mode SDL --> chiffre de 0 à 7.
*/

void generationAleatoireCombinaisonSecreteSDL(Combinaison* combinaisonSecrete, Parametres param)
{
    int i;
    initCombinaison(combinaisonSecrete, param);

    srand(time(NULL));
    for (i=0; i<combinaisonSecrete-> taille; i++)
    {
        combinaisonSecrete-> tab[i] = (rand()%param.nbCouleursUtilisables);
    }
}


/**
* \fn int parametresRespectes(int combTest, Parametres param)
* \brief La fonction parametresRespectes vérifie qu'un entier est bien compatible pour pouvoir être une combinaison.
* \return Retourne 1 si tout est respecté, 0 sinon.
*/

int parametresRespectes(int combTest, Parametres param)
{
    int i;
    int res=1;
    int modulo;
    int quotient=combTest;

    if(combTest>0)
    {
        if(param.taille == longueurEntier (combTest))
        {
            res=0;

            for(i=1; i<=param.taille; i++)
            {
                modulo=quotient%10;

                if((modulo<1) || (modulo > param.nbCouleursUtilisables))
                {
                    res = res+1;
                }
                quotient = quotient/10;
            }
        }
    }

    if(res == 0)
    {
        return (1);
    }

    else
    {
        return (0);
    }

}


/**
* \fn Combinaison saisieCombinaisonSDL(Parametres param, SDL_Surface *screen, int* notechap, int x, int y)
* \brief La procédure saisieCombinaisonSDL permet de saisir la combinaison à tester en mode SDL.
*/

Combinaison saisieCombinaisonSDL(Parametres param, SDL_Surface *screen, int* notechap, int x, int y)
{
    int continuer=1;
    int j=0;
    Combinaison comb;
    int largeurCase;

    SDL_Event event;

    /*Initialisation du tableau qui servira pour la combinaison*/
    initCombinaison(&comb,param);

    if (param.taille == 3)
    {
        largeurCase = 200;
    }

    else if (param.taille == 4)
    {
        largeurCase = 150;
    }

    else
    {
        largeurCase=120;
    }

    while(continuer)
    {
        afficheCombinaison(param, screen, comb.tab, x, y);
        SDL_Rect contour1 = {j*largeurCase+x, y, 4, 58};
        SDL_Rect contour2 = {(j+1)*largeurCase-6+x, y, 4, 57};
        SDL_Rect contour3 = {j*largeurCase+x, y, largeurCase-2, 4};
        SDL_Rect contour4 = {j*largeurCase+x, y+54, largeurCase-2, 4};

        SDL_FillRect(screen, &contour1, 0xFFFFFF);
        SDL_FillRect(screen, &contour2, 0xFFFFFF);
        SDL_FillRect(screen, &contour3, 0xFFFFFF);
        SDL_FillRect(screen, &contour4, 0xFFFFFF);

        SDL_Flip(screen);

        do
        {
            SDL_WaitEvent(&event);
        }
        while(event.type != SDL_KEYDOWN && event.type != SDL_QUIT);

        if(event.type == SDL_QUIT)
        {
            continuer = 0;
            *notechap = 0;
        }

        else if (event.type == SDL_KEYDOWN)
        {

            switch ( event.key.keysym.sym )
            {
                case SDLK_UP:

                    comb.tab[j] = (comb.tab[j]+1)%param.nbCouleursUtilisables;
                    break;

                case SDLK_DOWN:
                    comb.tab[j] = (comb.tab[j]+param.nbCouleursUtilisables-1)%param.nbCouleursUtilisables;
                    break;

                case SDLK_RIGHT:
                    if(j<param.taille-1)
                    {
                        j=j+1;

                    }
                    break;

                case SDLK_LEFT:
                    if(j>0)
                    {
                        j=j-1;

                    }
                    break;

                case SDLK_RETURN:
                   continuer=0;
                   break;

                case SDLK_ESCAPE:
                   continuer=0;
                   *notechap=0;
                   break;

                default:
                break;
            }
        }
    }
    return comb;
}



/**
* \fn void afficheCombinaison(Parametres param, SDL_Surface *screen, int* comb, int x, int y)
* \brief La procédure afficheCombinaison permet d'afficher une combinaison.
*        L'emplacement est défini par les coordonnées x, y.
*/

void afficheCombinaison(Parametres param, SDL_Surface *screen, int* comb, int x, int y)
{
    /*Définition des couleurs*/
    uint32_t bleuFonce = SDL_MapRGB(screen->format, 0x06, 0x02, 0xFF);
    uint32_t rouge = SDL_MapRGB(screen->format, 0xFF, 0x00, 0x00);
    uint32_t jaune = SDL_MapRGB(screen->format, 0xEE, 0xFF, 0x00);
    uint32_t vert = SDL_MapRGB(screen->format, 0x0B, 0xE2, 0x00);
    uint32_t orange = SDL_MapRGB(screen->format, 0xFF, 0x9C, 0x07);
    uint32_t violet = SDL_MapRGB(screen->format, 0xB2, 0x03, 0x5B);
    uint32_t marron = SDL_MapRGB(screen->format, 0xB2, 0x79, 0x33);
    uint32_t bleuClair = SDL_MapRGB(screen->format, 0x00, 0xFF, 0xF6);

    int i=0;
    int largeurCase=0;
    int hauteurCase=60;

    /* Initialisation de la largeur d'une case en fonction du niveau (3, 4 ou 5)*/
    if (param.taille == 3)
    {
        largeurCase = 200;
    }

    else if (param.taille == 4)
    {
        largeurCase = 150;
    }

    else
    {
        largeurCase=120;
    }


    for(i=0; i<param.taille; i++)
    {

        SDL_Rect place = {(i*largeurCase + x), y, largeurCase-2, hauteurCase-2};

        if (comb[i] == 0)
        {
            SDL_FillRect(screen, &place, bleuFonce);
        }

        else if (comb[i] == 1)
        {
            SDL_FillRect(screen, &place, rouge);
        }

        else if (comb[i] == 2)
        {
            SDL_FillRect(screen, &place, jaune);
        }

        else if (comb[i] == 3)
        {
            SDL_FillRect(screen, &place, vert);
        }

        else if (comb[i] == 4)
        {
            SDL_FillRect(screen, &place, orange);
        }

        else if (comb[i] == 5)
        {
            SDL_FillRect(screen, &place, violet);
        }

        else if (comb[i] == 6)
        {
            SDL_FillRect(screen, &place, marron);
        }

        else if (comb[i] == 7)
        {
            SDL_FillRect(screen, &place, bleuClair);
        }
    }
     SDL_Flip(screen);
}
